/**
 * 
 */
package de.htwg.ppt2html5.extraction;


import java.io.File;
import java.util.List;

import org.docx4j.dml.CTRegularTextRun;
import org.docx4j.dml.CTTextLineBreak;
import org.docx4j.dml.CTTextParagraph;
import org.docx4j.dml.STShapeType;
import org.docx4j.openpackaging.exceptions.InvalidFormatException;
import org.docx4j.openpackaging.packages.PresentationMLPackage;
import org.docx4j.openpackaging.parts.PartName;
import org.docx4j.openpackaging.parts.ThemePart;
import org.docx4j.openpackaging.parts.PresentationML.SlidePart;
import org.docx4j.openpackaging.parts.WordprocessingML.ImageJpegPart;
import org.pptx4j.pml.CTGraphicalObjectFrame;
import org.pptx4j.pml.CTRel;
import org.pptx4j.pml.CxnSp;
import org.pptx4j.pml.GroupShape;
import org.pptx4j.pml.Pic;
import org.pptx4j.pml.STPlaceholderType;
import org.pptx4j.pml.Shape;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.htwg.ppt2html5.export.CSSBuilder;
import de.htwg.ppt2html5.export.Export;
import de.htwg.ppt2html5.export.HTMLBuilder;
import de.htwg.ppt2html5.export.IExport;
import de.htwg.ppt2html5.export.IHTMLBuilder;


/**
 * this class is used for interpreting the data incoming from the Extractor class.
 * 
 * @author Roman Allenov
 */
public class Interpreter implements IInterpreter {

   private static Logger log = LoggerFactory.getLogger(Interpreter.class);
   private Slide         slide;
   private Presentation  presentation;
   private IHTMLBuilder  htmlBuilder;
   private CSSBuilder    cssBuilder;
   private IExport       export;


   public Interpreter() {

      this.htmlBuilder = new HTMLBuilder();
      this.cssBuilder = new CSSBuilder();
      this.export = new Export();
      this.presentation = new Presentation();
   }


   /**
    * this method prepares the slides for the export to use
    * 
    * @param pmlPackage
    *        - the Presentation package
    * @param slidesList
    *        - the list of slide parts mapped by <slideX, Slide>
    * @return - returns a map with slide index and the slides
    */
   public Presentation prepareSlides(PresentationMLPackage pmlPackage,
                                     List<SlidePart> slidesList)
   {
      try {

         // if a theme is set/available
         if (pmlPackage.getParts().get(new PartName("/ppt/theme/theme1.xml")) != null) {
            ThemePart theme = (ThemePart)pmlPackage.getParts().get(new PartName(
                                                                                "/ppt/theme/theme1.xml"));
            presentation.setFont(theme.getFontScheme());
            presentation.getThemes().add(theme);

            // the background image: not every design has only 1 image
            ImageJpegPart image = (ImageJpegPart)pmlPackage.getParts().get(new PartName(
                                                                                        "/ppt/media/image1.jpeg"));
            presentation.setBackgroundIMG(image);
         }
      }
      catch (InvalidFormatException e) {
         log.error(e.getMessage());
      }

      for (SlidePart slidePart : slidesList) {
         slide = new Slide();
         slide.setSlide(slidePart);
         slide.setSlideLayout(slidePart.getSlideLayoutPart());
         slide.setSlideMaster(slidePart.getSlideLayoutPart().getSlideMasterPart());
         slide.setSlideNotes(slidePart.getNotesSlidePart());
         slide.setSlideNotes(slidePart.getNotesSlidePart());

         // get the shapes from the shapetree of current slide
         // Shape, GroupShape, CTGraphicalObjectFrame, CxnSp, Pic, CTRel - objects possible
         List<Object> slideObjects = getShapesOrFrames();

         for (Object object : slideObjects) {
            if (object instanceof Shape) {
               handleShapeType(object);
            }
            else if (object instanceof GroupShape) {
               handleGroupShapeType(object);
            }
            else if (object instanceof CTGraphicalObjectFrame) {
               handleCTGraphObjFrameType(object);
            }
            else if (object instanceof CxnSp) {
               handleCxnSpType(object);
            }
            else if (object instanceof Pic) {
               handlePicType(object);
            }
            else if (object instanceof CTRel) {
               handleCTRelType(object);
            }
         }

         presentation.getSlides().add(slide);
      }

      // extra slidesize for export
      presentation.setSlideSize(pmlPackage.getMainPresentationPart().getJaxbElement().getSldSz());
      presentation.setTitle(pmlPackage.getTitle());

      // if(pmlPackage.getMainPresentationPart().getContents())

      return presentation;

   }


   /**
    * this method invokes the export of the HTML file.
    * 
    * @param slideList
    *        - the map retrieved from prepareSlides()
    * @param fileName
    *        - the file to be saved
    * @param filePath
    *        - the path of the fileName to be saved
    * @return - returns the error value
    */
   public int triggerExport(Presentation pres, File fileName, String alignment)
   {
      String css = cssBuilder.buildCSS(pres, pres.getSlides(), fileName);
      String html = htmlBuilder.buildHTML(pres.getSlides(), presentation.getTitle(),
                                          presentation.getSlideSize(), alignment);

      return export.export(html, css, fileName, presentation.getBackgroundIMG());

   }


   private void handleCTRelType(Object object)
   {
      CTRel ctRel = (CTRel)object;
      // debug
      log.info("betrachte: " + ctRel.getId() + " (CTRel)");
      slide.getRelations().put("rel" + ctRel.getId(), ctRel);
   }


   private void handlePicType(Object object)
   {
      Pic pic = (Pic)object;

      // debug
      log.info("betrachte: " + pic.getNvPicPr().getCNvPr().getName() + " (Pic)");
      STPlaceholderType placeholder = null;

      // get placeholder for pic
      if (pic.getNvPicPr().getNvPr() != null) {
         if (pic.getNvPicPr().getNvPr().getPh() != null) {
            // if placeholder is not null, get placeholder
            placeholder = pic.getNvPicPr().getNvPr().getPh().getType();
         }
         if (placeholder != null && placeholder.value().equals(STPlaceholderType.PIC.value())) {
            slide.getPics().put(placeholder.value() + pic.getNvPicPr().getCNvPr().getId(), pic);
         }
      }
      else {
         slide.getPics().put("pic" + pic.getNvPicPr().getCNvPr().getId(), pic);
      }
   }


   private void handleCxnSpType(Object object)
   {
      CxnSp cxnSp = (CxnSp)object;
      // debug
      log.info("betrachte: " + cxnSp.getNvCxnSpPr().getCNvPr().getName() + " (CxnSp)");
      STShapeType shapeType = null;
      if (cxnSp.getSpPr() != null) {
         shapeType = cxnSp.getSpPr().getPrstGeom().getPrst();
         // add to shapes list
         slide.getConnectors().put(shapeType.value(), cxnSp);
      }
   }


   private void handleCTGraphObjFrameType(Object object)
   {
      CTGraphicalObjectFrame graphicFrame = (CTGraphicalObjectFrame)object;

      // debug
      log.info("betrachte: " + graphicFrame.getNvGraphicFramePr().getCNvPr().getName()
               + " (graphFrame)");
      slide.getGraphFrames().put("graph"
                                          + graphicFrame.getNvGraphicFramePr().getCNvPr().getId(),
                                 graphicFrame);
   }


   private void handleGroupShapeType(Object object)
   {
      GroupShape grpSp = (GroupShape)object;

      // debug
      log.info("betrachte: " + grpSp.getNvGrpSpPr().getCNvPr().getName() + " (grpShape)");

      List<Object> groupShapeObjects = grpSp.getSpOrGrpSpOrGraphicFrame();
      for (Object object2 : groupShapeObjects) {
         handleShapeType(object2);
      }
   }


   private void handleShapeType(Object object)
   {
      Shape sp = (Shape)object;

      // debug
      log.info("betrachte: " + sp.getNvSpPr().getCNvPr().getName());
      // placeholderType can have values of {@link STPlaceholderType}
      STPlaceholderType placeholderType = null;
      // shapeType can have values of STShapeType
      STShapeType shapeType = null;

      // get the placeholder type to determine what type the shape is
      if (sp.getNvSpPr().getNvPr().getPh() != null) {
         // if placeholder is not null, get placeholder
         placeholderType = sp.getNvSpPr().getNvPr().getPh().getType();

         // listshape
         long idx = sp.getNvSpPr().getNvPr().getPh().getIdx();
         // 0 is default
         if (idx != 0) {
            slide.getphShapes().put("list" + idx, sp);
         }
         /**
          * centered title & sub title occur once on the first slide.
          */
         // Check if it's a (Centered) Title

         if (placeholderType != null
             && placeholderType.value().equals(STPlaceholderType.CTR_TITLE.value())) {

            // get a list of CTTextParagraphs <a:p>
            List<CTTextParagraph> paragraphList = sp.getTxBody().getP();

            String ctrTitle = "";
            ctrTitle = getTitleFromP(paragraphList, ctrTitle);
            slide.setCtrTitle(ctrTitle);
            slide.getphShapes().put(placeholderType.value(), sp);
         }

         // else if it's a subtitle
         else if (placeholderType != null
                  && placeholderType.value().equals(STPlaceholderType.SUB_TITLE.value())) {
            // similar to above: get a list of CTTextParagraphs <a:p>
            List<CTTextParagraph> paragraphList = sp.getTxBody().getP();

            String subtitle = "";
            subtitle = getTitleFromP(paragraphList, subtitle);
            slide.setSubTitle(subtitle);
            slide.getphShapes().put(placeholderType.value(), sp);
         }

         // normal slide title
         else if (placeholderType != null
                  && placeholderType.value().equals(STPlaceholderType.TITLE.value())) {
            List<CTTextParagraph> paragraphList = sp.getTxBody().getP();
            String slideTitle = "";
            slideTitle = getTitleFromP(paragraphList, slideTitle);
            slide.setSlideTitle(slideTitle);
            slide.getShapes().put(placeholderType.value(), sp);
         }
         // other shape like date&time or slideNum
         else if (placeholderType != null) {
            STPlaceholderType typeKey = STPlaceholderType.fromValue(placeholderType.value());
            slide.getphShapes().put(typeKey.value(), sp);
         }
      }
      /**
       * drawing shapes from {@link STShapeType}
       */
      else if (sp.getSpPr() != null) {
         shapeType = sp.getSpPr().getPrstGeom().getPrst();
         // add to shapes list
         slide.getShapes().put(shapeType.value(), sp);
         // CTTransform2D shape2D = shape.getSpPr().getXfrm();
         // CTShapeStyle shapeStyle = shape.getStyle();
      }
   }


   /**
    * This method gets the shapes of the Slide object. <br>
    * Possible objects type(s) are:
    * <ul>
    * <li>Shape</li>
    * <li>GroupShape</li>
    * <li>CTGraphicalObjectFrame</li>
    * <li>CxnSp</li>
    * <li>Pic</li>
    * <li>CTRe</li>
    * 
    * @return the list containing found shapes
    */
   private List<Object> getShapesOrFrames()
   {
      return slide.getSlide().getJaxbElement().getCSld().getSpTree().getSpOrGrpSpOrGraphicFrame();
   }


   /**
    * this method concatenates the <a:t> elements from the Title
    * 
    * @param paragraphList
    * @param title
    * @return the title
    */
   private String getTitleFromP(List<CTTextParagraph> paragraphList, String title)
   {
      for (CTTextParagraph a_p : paragraphList) {
         // list of <a:r>
         List<Object> textRun = a_p.getEGTextRun();
         for (Object a_r : textRun) {
            if (a_r instanceof CTRegularTextRun) {
               title += ((CTRegularTextRun)a_r).getT();
            }
            else if (a_r instanceof CTTextLineBreak) {
               title += System.getProperty("line.separator");
            }
         }
      }
      return title;
   }


   public Slide getSlide()
   {
      return slide;
   }

}
